/*

<This Java Class is part of the jMusic API>

Copyright (C) 2000 Andrew Sorensen & Andrew Brown

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or any
later version.

This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

*/

package jm.gui.wave;

import java.awt.*;

/**
 * This class displays floating point data passed to it, typically
 * read from the audio file or generated by s synthesis process.
 * It is mainly used in conjunction with the WaveView class.
 *
 * @author Andrew Brown
 */

public class WaveCanvas extends Canvas {
    // for double buffering
    public Image image = null;
    protected Graphics g;
    //private SampleIn si;
    private float[] data;
    private int segmentSize = 0;
    private int resolution = 125;
    private int height = 200;
    private int amplitude = 1;
    private boolean resized = false;
    private Color waveColor = Color.darkGray;
    private Color backgroundColor = Color.white;
    private int sampleStart = 0;
    private int waveSize;
    private boolean fastDraw = false;

    public WaveCanvas() {
        super();
    }

    /*
     * Fill the data array with the sample values to be displayed
     */
    public void setData(float[] data) {
        this.data = data;
        this.segmentSize = data.length;
    }

    /**
     * Change the horizontal zoom size of the displayed waveform.
     *
     * @param size The new resolution value.
     */
    public void setResolution(int size) {
        if (size > 0) this.resolution = size;
        setFastDraw(true);
        repaint();
        setFastDraw(false);
        repaint();
    }

    /**
     * Change the vertical zoom size of the displayed waveform.
     *
     * @param val The new height value.
     */
    public void setHeight(int val) {
        if (val > 0) this.height = val;
        this.setSize(new Dimension(600, height + 1));
        resized = true;
        repaint();
    }

    /**
     * Set the paint routine to a quick render algorithm or full fidelity.
     *
     * @param state - True = quick rendering, false = more accurate rendering
     */
    public void setFastDraw(boolean state) {
        this.fastDraw = state;
    }


    /**
     * Change the waveform amplitude magnification of the displayed waveform.
     *
     * @param size The new resolution value.
     */
    public void setAmplitude(int size) {
        if (size > 0) this.amplitude = size;
        repaint();
    }

    /*
    * Specify the number of samples in one channel of the file.
    */
    public void setWaveSize(int size) {
        this.waveSize = size;
    }

    /*
    * Notify instance if it needs to draw at a new size.
    * @param boolean True to redraw.
    */
    public void setResized(boolean val) {
        this.resized = val;
        setFastDraw(true);
        repaint();
        setFastDraw(false);
        repaint();

    }

    public void toggleColor() {
        if (waveColor == Color.darkGray) {
            waveColor = Color.green;
            backgroundColor = Color.darkGray;
        } else {
            waveColor = Color.darkGray;
            backgroundColor = Color.white;
        }
        //this.setBackground(backgroundColor);
        repaint();
    }

    public void paint(Graphics graphics) {
        // set cursor
        this.setCursor(new Cursor(Cursor.WAIT_CURSOR));
        // set up for double buffering
        if (image == null || resized) {
            image = this.createImage(this.getSize().width, this.getSize().height);
            g = image.getGraphics();
            resized = false;
        }
        clearImage(g);
        int h2 = height / 2 - 1;
        float drawMax, drawMin, currData;
        // mid line
        g.setColor(Color.black);
        g.drawLine(0, h2, this.getSize().width, h2);
        // draw wave

        //int pixCount = Math.min(data.length-resolution, this.getSize().width * resolution);
        final int xMax = this.getSize().width * resolution;
        final int dataMax = data.length - resolution;
        g.setColor(waveColor);
        if (resolution == 1) {
            for (int i = 0; i < xMax && i < dataMax; i++) {
                currData = data[i];
                g.drawLine(i, (int) (h2 - currData * h2 * amplitude),
                        i, (int) (h2 - currData * h2 * amplitude));
            }
        } else {
            int xPosition = 0;
            float max = 0.0f;
            float min = 0.0f;

            for (int i = 0; i < xMax && i < dataMax; i += resolution) {
                currData = data[i];
                // fast update? - rarerly used in practice.
                if (fastDraw) {
                    g.drawLine(xPosition++, (int) (h2 - currData * h2 * amplitude),
                            xPosition, (int) (h2 - currData * h2 * amplitude));
                } else {
                    // max and min
                    max = 0.0f;
                    min = 0.0f;
                    for (int j = 0; j < resolution; j++) {
                        if (data[i + j] > max) max = data[i + j];
                        if (data[i + j] < min) min = data[i + j];
                    }
                    // highest and lowest curve values
                    if (resolution > 8) {
                        drawMax = Math.max(currData, data[i + resolution]);
                        drawMin = Math.min(currData, data[i + resolution]);
                        if (max > 0.0f) g.drawLine(xPosition,
                                (int) (h2 - drawMax * h2 * amplitude),
                                xPosition, (int) (h2 - max * h2 * amplitude));
                        if (min < 0.0f) g.drawLine(xPosition,
                                (int) (h2 - drawMin * h2 * amplitude),
                                xPosition, (int) (h2 - min * h2 * amplitude));
                    }
                    // draw wave
                    g.drawLine(xPosition++, (int) (h2 - currData * h2 * amplitude),
                            xPosition, (int) (h2 - data[i + resolution] * h2 * amplitude));
                }
            }
        }
        // base line
        g.setColor(Color.lightGray);
        //g.drawLine(0, 0, this.getSize().width, 0);
        g.drawLine(0, height, this.getSize().width, height);

		/* Draw completed buffer to g */
        graphics.drawImage(image, 0, 0, null);
        clearImage(g);
        // reset cursor
        this.setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
    }

    private void clearImage(Graphics g) {
        // clear image
        g.setColor(backgroundColor);
        g.fillRect(0, 0, getSize().width, getSize().height);
        g.setColor(waveColor);
    }
}